﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;

namespace ArchiveLink
{
    internal class DirectorySyncer
    {
        private readonly DriveInfo _source;
        private readonly DriveEventArgs _driveEventArgs;
        private readonly DirectoryInfo _target;
        private readonly FileInfo _mockFile;
        private SyncerFactory _syncerFactory;

        public DirectorySyncer(DriveInfo source, DriveEventArgs driveEventArgs, DirectoryInfo target, FileInfo mockFile)
        {
            _source = source;
            _driveEventArgs = driveEventArgs;
            _target = target;
            _mockFile = mockFile;
            _syncerFactory= new SyncerFactory(_mockFile);
        }

        public void DoLink()
        {
            if (_source == null)
            {
                throw new ArgumentException("Source is null.");
            }
            if (_target == null)
            {
                throw new ArgumentException("Target is null.");
            }
            if (string.IsNullOrEmpty(_source.VolumeLabel))
            {
                if (_driveEventArgs.Serial > 0)
                {
                    DoLink(_source.RootDirectory, _target, _driveEventArgs.Serial.ToString(), _mockFile, true);
                }
            }
            else
            {
                DoLink(_source.RootDirectory, _target, _source.VolumeLabel, _mockFile, true);
            }
        }


        private void DoLink(DirectoryInfo source, DirectoryInfo target, string targetName, FileInfo mockFile, bool root = false)
        {
            var targetSubDir = new DirectoryInfo(Path.Combine(target.FullName, targetName));
            try
            {
                foreach (var subDir in source.GetDirectories())
                {
                    if (!subDir.Attributes.HasFlag(FileAttributes.Hidden))
                    {
                        DoLink(subDir, targetSubDir, subDir.Name, mockFile);
                    }
                }
            }
            catch (UnauthorizedAccessException)
            {
            }


            if (!targetSubDir.Exists)
            {
                targetSubDir.Create();
            }
            if (root)
            {
                var mirrorInfoFile = new FileInfo(Path.Combine(targetSubDir.FullName, "mirror.info"));
                if (mirrorInfoFile.Exists)
                {
                    //TODO Update
                }
                else
                {
                    MirrorInfo mirrorInfo = new MirrorInfo();
                    mirrorInfo.DriveLetter = source.Root.Name;
                    mirrorInfo.LastUpdate = DateTime.Now.ToString();
                    mirrorInfo.UserFolder = false;
                    //mirrorInfo.VolumeName 
                    //mirrorInfo.VolumeSerial

                    var fileStream = mirrorInfoFile.Open(FileMode.Create, FileAccess.Write);
                    XmlSerializer serialzer = new XmlSerializer(typeof(MirrorInfo));
                    serialzer.Serialize(fileStream, mirrorInfo);
                    fileStream.Close();
                    mirrorInfoFile.Attributes = FileAttributes.Hidden | FileAttributes.System;
                }
            }
            try
            {

                foreach (var file in source.GetFiles())
                {
                    if (!file.Attributes.HasFlag(FileAttributes.Hidden))
                    {
                        var syncer = _syncerFactory.GetSyncerImpl(file.FullName);
                        syncer.CreateMirror(Path.Combine(targetSubDir.FullName, file.Name));
                        //DirectoryLinker.CreateFileLink(mockFile, Path.Combine(targetSubDir.FullName, file.Name));
                    }
                }
            }
            catch (UnauthorizedAccessException)
            {
            }

        }

        public void DoClean()
        {
            if (string.IsNullOrEmpty(_source.VolumeLabel))
            {
                if (_driveEventArgs.Serial > 0)
                {
                    DoClean(_source.RootDirectory, new DirectoryInfo(Path.Combine(_target.FullName, _driveEventArgs.Serial.ToString())));
                }
            }
            else
            {
                DoClean(_source.RootDirectory, new DirectoryInfo(Path.Combine(_target.FullName, _source.VolumeLabel)));
            }
        }

        private void DoClean(DirectoryInfo sourceDir, DirectoryInfo archiveDir)
        {
            foreach (var subDir in archiveDir.GetDirectories())
            {
                var srcSubDir = sourceDir.GetDirectories(subDir.Name);
                if (srcSubDir.Length == 0)
                {
                    subDir.Delete(true);
                }
                else
                {
                    DoClean(srcSubDir[0], subDir);
                }

            }

            foreach (var file in archiveDir.GetFiles())
            {
                if (!File.Exists(Path.Combine(sourceDir.FullName, file.Name)))
                {
                    file.Delete();
                }
            }
        }


    }
}
